////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_X86_SSE2_BASIC_INL__
#define __WN_MATH_INTERNAL_X86_SSE2_BASIC_INL__

#ifndef __WN_MATH_BASIC_H__
    #error "x86/SSE2/WNBasic.inl should never be included directly. Please include WNBasic.h instead"
#elif !defined _WN_X86
    #error "x86/SSE2/WNBasic.inl has been included on a non x86 platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "x86/SSE2/WNBasic.inl has been included when extensions are disabled. Please rectify this."
#elif !defined __WN_MATH_SSE2_ALLOWED
    #error "x86/SSE2/WNBasic.inl has been included when SSE2 is not allowed. Please rectify this."
#endif

#include <emmintrin.h>

namespace WNMath {
    namespace __WNInternal {
        __WN_MATH_SSE2_INLINE WN_FLOAT64 __WNSqrtSSE2(const WN_FLOAT64& _number) {
            __m128d number = _mm_load_sd(&_number);

            return(_mm_cvtsd_f64(_mm_sqrt_sd(_mm_setzero_pd(), number)));
        }

        __WN_MATH_SSE2_INLINE WN_FLOAT64 __WNInvSqrtSSE2(const WN_FLOAT64& _number) {
            __m128d number = _mm_load_sd(&_number);
            __m128d sqrt = _mm_sqrt_sd(_mm_setzero_pd(), number);
            __m128d invSqrt = _mm_div_sd(_mm_set1_pd(1.0), sqrt);

            return(_mm_cvtsd_f64(invSqrt));
        }

        __WN_MATH_SSE2_INLINE WN_FLOAT64 __WNMinSSE2(const WN_FLOAT64& _first, const WN_FLOAT64& _second) {
            __m128d first = _mm_load_sd(&_first);
            __m128d second = _mm_load_sd(&_second);

            return(_mm_cvtsd_f64(_mm_min_sd(first, second)));
        }

        __WN_MATH_SSE2_INLINE WN_FLOAT64 __WNMaxSSE2(const WN_FLOAT64& _first, const WN_FLOAT64& _second) {
            __m128d first = _mm_load_sd(&_first);
            __m128d second = _mm_load_sd(&_second);

            return(_mm_cvtsd_f64(_mm_max_sd(first, second)));
        }

        __WN_MATH_SSE2_INLINE WN_FLOAT64 __WNClampSSE2(const WN_FLOAT64& _number, const WN_FLOAT64& _min, const WN_FLOAT64& _max) {
            __m128d number = _mm_load_sd(&_number);
            __m128d min = _mm_load_sd(&_min);
            __m128d max = _mm_load_sd(&_max);

            return(_mm_cvtsd_f64(_mm_min_sd(_mm_max_sd(number, min), max)));
        }
    }
}

#endif // __WN_MATH_INTERNAL_X86_SSE2_BASIC_INL__